/*
 * Copyright (c) 2015 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <zephyr/kernel.h>
#include <zephyr/shell/shell.h>
#include <version.h>
#include <zephyr/logging/log.h>
#include <stdlib.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>
#include <ctype.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/util.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/device.h>
#include <zephyr/shell/shell_uart.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/task_wdt/task_wdt.h>
#include <zephyr/sys/reboot.h>
#include "include/Ymodem.h"
#include "include/common.h"
/*********************************************************************
 * 需要的标志定义
 *********************************************************************/
LOG_MODULE_REGISTER(common);
#define WDT_MAX_WINDOW  1000U

#define APP_OFFSET_ADDR  0x00020000 //定义app的偏移地址，相应得要在app工程文件menuconfig中修改
#define BOOT_SIZE_SIGN_OFFSET_ADDR  0x000EFFE0  //flash的一个区域划为boot程序大小标记
#define APP_SIZE_SIGN_OFFSET_ADDR  0x000EFFF0  //flash的一个区域划为app程序大小标记

#define DOWNLOAD_THREAD_PRIO   7
#define DOWNLOAD_STACKSIZE	10*1024
#define DOWNLOAD_IDLE 0
#define DOWNLOAD_BOOT 1
#define DOWNLOAD_APP  2
#define DOWNLOAD_DEST 3

#if DT_NODE_HAS_STATUS(DT_ALIAS(watchdog0), okay)
#define WDT_NODE DT_ALIAS(watchdog0)
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_window_watchdog)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(st_stm32_window_watchdog)
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_watchdog)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(st_stm32_watchdog)
#elif DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_wdt)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_nrf_wdt)
#elif DT_HAS_COMPAT_STATUS_OKAY(espressif_esp32_watchdog)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(espressif_esp32_watchdog)
#elif DT_HAS_COMPAT_STATUS_OKAY(silabs_gecko_wdog)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(silabs_gecko_wdog)
#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_kinetis_wdog32)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nxp_kinetis_wdog32)
#elif DT_HAS_COMPAT_STATUS_OKAY(microchip_xec_watchdog)
#define WDT_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(microchip_xec_watchdog)
#else
#define WDT_NODE DT_INVALID_NODE
#endif
typedef volatile uint32_t  vu32;

/*********************************************************************
 * 函数声明
 *********************************************************************/
void download_function(void *p1, void *p2, void *p3);
void shell_reinit_trigger(void);
void shell_uninit_download_cb(const struct shell *shell, int res);
/*********************************************************************
 * 全局变量及外部变量声明
 *********************************************************************/
uint32_t DOWN_DEST_ADDR = 0x080E0000; //设置一个默认的flash目标下载地址，在shell中定义dest
extern uint32_t CUR_BASE_ADDR;
//看门狗资源
const struct device *const hw_wdt_dev = DEVICE_DT_GET_OR_NULL(WDT_NODE);
K_THREAD_DEFINE(wdt_thread, 1024, wdt_handle, NULL, NULL, NULL, 1, 0, 0);
int task_wdt_id;
//download线程资源
uint8_t download_type;
K_THREAD_DEFINE(download_tid,DOWNLOAD_STACKSIZE,download_function,NULL, NULL, NULL,
                DOWNLOAD_THREAD_PRIO, 0, 0);


/*******************************************************************************
  * @函数名称	task_wdt_callback
  * @函数说明   看门狗触发重启前的回调函数
  * @输入参数   看门狗通道和数据
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
static void task_wdt_callback(int channel_id, void *user_data)
{
	printk("Task watchdog channel %d callback, thread: %s\n",
		channel_id, k_thread_name_get((k_tid_t)user_data));
	LOG_ERR("Task watchdog channel %d callback, thread: %s\n",
		channel_id, k_thread_name_get((k_tid_t)user_data));
	
	sys_reboot(SYS_REBOOT_COLD);
}

/*******************************************************************************
  * @函数名称	wdt_handle
  * @函数说明   喂狗线程
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void wdt_handle(void)
{
	while (true) {			
		task_wdt_feed(task_wdt_id);
		k_sleep(K_MSEC(1000));
	}
}

/*******************************************************************************
  * @函数名称	watchdog_init
  * @函数说明   看门狗初始化
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
int watchdog_init(void)
{
	int ret;	

	if (!device_is_ready(hw_wdt_dev)) {
		printk("Hardware watchdog not ready; ignoring it.\n");
	}

	ret = task_wdt_init(hw_wdt_dev);
	if (ret != 0) {
		printk("task wdt init failure: %d\n", ret);
		LOG_ERR("task wdt init failure: %d\n", ret);
		return -1;
	}


	/* passing NULL instead of callback to trigger system reset */
	task_wdt_id = task_wdt_add(1100U, task_wdt_callback,
		(void *)k_current_get());

	return 0;
}


/*******************************************************************************
  * @函数名称	download_function
  * @函数说明   下载任务线程
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void download_function(void *p1, void *p2, void *p3)
{
	static const struct device *flash_device =
		DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_flash_controller));
	int32_t size;
	uint8_t buf[10];
	k_thread_suspend(download_tid); //重启完默认不开启该线程
	while(1){
		switch(download_type){
			case DOWNLOAD_IDLE:
				k_sleep(K_MSEC(100));
			break;
			case DOWNLOAD_APP:
				shell_uninit(shell_backend_uart_get_ptr(), shell_uninit_download_cb);
				k_sleep(K_MSEC(50));
				size = SerialDownload(APP_OFFSET_ADDR);
				k_sleep(K_MSEC(50));
				int2str(size,buf);
				if(size > 0){
					flash_erase(flash_device,APP_SIZE_SIGN_OFFSET_ADDR,10);
					flash_write(flash_device,APP_SIZE_SIGN_OFFSET_ADDR,buf,10);
				}
				shell_reinit_trigger();
				download_type = DOWNLOAD_IDLE;
				break;

			case DOWNLOAD_DEST:
				shell_uninit(shell_backend_uart_get_ptr(), shell_uninit_download_cb);
				k_sleep(K_MSEC(50));
				SerialDownload(DOWN_DEST_ADDR);
				k_sleep(K_MSEC(50));
				shell_reinit_trigger();
				download_type = DOWNLOAD_IDLE;
				break;
			default:
				break;
		}
		k_thread_suspend(download_tid);
	}
}

/*******************************************************************************
  * @函数名称	jump_to_addr
  * @函数说明   跳转addr
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void jump_to_addr(uint32_t addr)
{	LOG_INF("\r\ninput jump to addr\r\n");
	if(((*(vu32*)addr)&(0x2FFF0000 - (addr & 0xf<<16)))==0x20000000)	//检查栈顶地址是否合法.
	{ 
		LOG_INF("\r\njump ready...\r\n");
		const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
		uart_irq_tx_disable(dev);
		uart_irq_rx_disable(dev);
		task_wdt_delete(task_wdt_id);
		//__set_MSP(*(vu32*)addr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		(((void(*)(void))*(vu32*)(addr+4)))();		//用户代码区第二个字为程序开始地址(复位地址)										
	}
	else{
		LOG_INF("jump error,please check rom\r\n\r\n");
		shell_reinit_trigger();
	}
}


uint16_t CRC16_ModBus(uint8_t *puchMsg, uint16_t usDataLen)
{
	
	uint16_t uCRC = 0xffff;//CRC寄存器
	
	for(uint16_t num=0;num<usDataLen;num++){
		uCRC = (*puchMsg++)^uCRC;//把数据与16位的CRC寄存器的低8位相异或，结果存放于CRC寄存器。
		for(uint8_t x=0;x<8;x++){	//循环8次
			if(uCRC&0x0001){	//判断最低位为：“1”
				uCRC = uCRC>>1;	//先右移
				uCRC = uCRC^0xA001;	//再与0xA001异或
			}else{	//判断最低位为：“0”
				uCRC = uCRC>>1;	//右移
			}
		}
	}
	return uCRC;//返回CRC校验值
}


/*******************************************************************************
  * @函数名称	str2int  
  * @函数说明   通用 str转int 
  * @输入参数   str
  * @输出参数   无
  * @返回参数   int
*******************************************************************************/
int str2int(const char *str)
    {
        int temp = 0;
        const char *ptr = str;  //ptr保存str字符串开头
  
        if (*str == '-' || *str == '+')  //如果第一个字符是正负号，
       {                      //则移到下一个字符
           str++;
       }
       while(*str != 0)
       {
           if ((*str < '0') || (*str > '9'))  //如果当前字符不是数字
           {                       //则退出循环
               break;
           }
           temp = temp * 10 + (*str - '0'); //如果当前字符是数字则计算数值
           str++;      //移到下一个字符
       }  
       if (*ptr == '-')     //如果字符串是以“-”开头，则转换成其相反数
       {
           temp = -temp;
       }
 
       return temp;
   }
 /*******************************************************************************
  * @函数名称	int2str
  * @函数说明   通用int转str
  * @输入参数   int str地址
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void int2str(int n, char *str)
    {
        char buf[10] = "";
        int i = 0;
        int len = 0;
        int temp = n < 0 ? -n: n;  // temp为n的绝对值
  
       if (str == NULL)
       {
           return;
       }
       while(temp)
       {
           buf[i++] = (temp % 10) + '0';  //把temp的每一位上的数存入buf
           temp = temp / 10;
       }
  
       len = n < 0 ? ++i: i;  //如果n是负数，则多需要一位来存储负号
       str[i] = 0;            //末尾是结束符0
       while(1)
       {
           i--;
           if (buf[len-i-1] ==0)
           {
               break;
           }
           str[i] = buf[len-i-1];  //把buf数组里的字符拷到字符串
       }
       if (i == 0 )
       {
           str[i] = '-';          //如果是负数，添加一个负号
       }
   }

float my_atof(const char* str)
{
	float d = 10.0;
	float s = 0.0;
	bool flag = false;
	while (*str > '9' || *str < '.')
	{
		str++;
	}
	if (*str == '-')
	{
		flag = true;
		str++;
	}
	if (!(*str>= '0' && *str <='9'))   //如果一开始并非数字，那么直接退出
	{
		return s;
	}
	while (*str >= '0' && *str <= '9' && *str != '.')  //计算小数点前几位
	{
		s = s * 10 + (*str - '0');
		str++;
	}
	if (*str == '.')  //之后为小数部分
		str++;
	while(*str >='0' && *str <= '9')
	{
		s = s + (*str - '0') / d;
		d *= 10.0;
		str++;
	}
	
	return s * (flag ? -1.0 : 1.0);

}